using System;
using System.Collections.Generic;
using Jarvis.Server.Persistence;
using System.Linq;

namespace Jarvis.Server.Providers
{
    public class LocationProvider : ILocationProvider
    {
        private ILocationRepository _locations;
        private ISensorDatasProvider _sdp;

        public LocationProvider(ISensorDatasProvider sdp, ILocationRepository locations)
        {
            if (sdp == null) throw new ArgumentNullException("sdp", "ISensorDataProvider dependency should be provided");
            if (locations == null) throw new ArgumentNullException("locations", "Repository dependency should be provided");
            _sdp = sdp;
            _locations = locations;


        }

        #region Implementation of ILocationProvider

        /// <summary>
        /// Location object referring to the location the agent is currently
        /// in (not stored in the db of known locations) all fields are not populated
        /// </summary>
        public Domain.Location CurrentLocation
        {
            get
            {

                var sensorDatas = _sdp.GetCurrentSensorDatas();
                var location = new Domain.Location() { LocationSensorDatas = sensorDatas };
                return location;
            }
        }

        /// <summary>
        /// Location object referring to the best fitting location present in the db.
        /// </summary>
        public Domain.Location ComputedLocation
        {
            get
            {
                var currentLocation = CurrentLocation;

                Domain.Location retval;
                using (var session = _locations.OpenSession())
                {
                    using (var tx = session.BeginTransaction())
                    {


                        var locations = from location in _locations.All()
                                        select location;

                        //Forces Loading from db by constructing list in order to use order by clause
                        var locationsList = locations.ToList().OrderBy(
                            location => location.LocationSensorDatas.DistanceFrom(currentLocation.LocationSensorDatas));

                        retval = locationsList.FirstOrDefault();
                    }
                }

                return retval;

            }
        }

        public IList<Domain.Location> KnownLocations
        {
            get
            {
                List<Domain.Location> retval;
                using (var session = _locations.OpenSession())
                {
                    retval = _locations.All().ToList();
                }
                return retval;
            }
        }

        /// <summary>
        /// Stores the location object in the db of known locations
        /// </summary>
        /// <param name="location">location object to store, required fields should be populated</param>
        public void StoreAsKnownLocation(Domain.Location location)
        {
            location.LocationSensorDatas = CurrentLocation.LocationSensorDatas;
            using (var session = _locations.OpenSession())
            {
                using (var tx = session.BeginTransaction())
                {
                    _locations.Add(location);
                    tx.Commit();
                }
            }
        }

        public void RemoveLocation(Domain.Location location)
        {
            using (var session = _locations.OpenSession())
            {
                using (var tx = session.BeginTransaction())
                {
                    _locations.Delete(_locations.FindBy(location.Id));
                    tx.Commit();
                }
            }
        }

        public void UpdateLocation(Domain.Location location)
        {
            using (var session = _locations.OpenSession())
            {
                using (var tx = session.BeginTransaction())
                {
                    location.LocationSensorDatas = _locations.FindBy(location.Id).LocationSensorDatas;
                    _locations.Merge(location);
                    tx.Commit();
                }
            }
        }

        #endregion
    }
}